package com.xyxeshang.es.music.backend.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.xyxeshang.es.music.backend.entity.Album;
import com.xyxeshang.es.music.backend.entity.Artist;
import com.xyxeshang.es.music.backend.entity.Song;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import us.codecraft.xsoup.Xsoup;

import java.io.IOException;
import java.util.*;

/**
 * @author xyx-Eshang
 * 本工具包仅用于学习交流，请勿用作任何商业用途。
 */
@Component
@SuppressWarnings("unchecked")
public class EsMusicUtil {
	private static final String COOKIE = "NMTID=00O9JZUzMMmzHH4wkAVoxJ6fA2qz_EAAAF9CZg1fw; path=/; domain=.music.163.com; Expires=Sat, 08 Nov 2031 11:25:09 GMT;";

	private static String audioQuality;

	private static Integer songListCount;

	public static String getAudioQuality() {
		return audioQuality;
	}

	@Value("${AUDIO_QUALITY}")
	public void setAudioQuality(String audioQuality) {
		EsMusicUtil.audioQuality = audioQuality;
	}

	public static Integer getSongListCount() {
		return songListCount;
	}

	@Value("${SONG_LIST_COUNT}")
	public void setSongListCount(Integer songListCount) {
		EsMusicUtil.songListCount = songListCount;
	}

	/**
	 * 底层函数：获取歌词
	 *
	 * @param songId
	 * @return
	 * @throws IOException
	 */
	private static String baseGetSongLyric(String songId) throws IOException {
		Map<String, Object> paramMap = new HashMap<>();
		paramMap.put("id", songId);
		Map<String, Object> response = HttpClientUtil.getWithParams("http://music.163.com/api/song/media", paramMap);
		String jsonResult = (String) response.get("body");
		JSONObject jsonObject = JSON.parseObject(jsonResult);

		// 有歌词时
		String lyric = jsonObject.getString("lyric");
		if (lyric != null) {
			return lyric;
		}
		// 无歌词时
		else {
			return "暂无歌词";
		}
	}

	/**
	 * 底层函数：搜索歌曲
	 *
	 * @param key
	 * @param offset
	 * @param limit
	 * @param type
	 * @return
	 * @throws IOException
	 */
	private static JSONObject baseSearchSong(String key, Integer offset, Integer limit, Integer type) throws
			IOException {
		// 剔除关键字中的空格、左括号、有括号
		key = key.replace(" ", "")
				.replace("（", "")
				.replace("(", "")
				.replace("）", "")
				.replace(")", "");

		String url = "http://music.163.com/api/search/pc";
		Map<String, Object> paramMap = new HashMap<>();
		paramMap.put("s", key);
		paramMap.put("offset", offset);
		paramMap.put("limit", limit);
		paramMap.put("type", type);

		// 发送请求
		Map<String, String> headerMap = new HashMap<>();
		// 携带Cookie
		headerMap.put("Cookie", EsMusicUtil.COOKIE);
		Map<String, Object> response = HttpClientUtil.postWithParams(headerMap, url, paramMap);
		String resultJson = (String) response.get("body");
		JSONObject jsonObject = JSON.parseObject(resultJson);
		Integer code = jsonObject.getInteger("code");

		// code为-462时，重新获取Set-Cookie
		if (code == -462) {
			Map<String, Object> headers = (Map<String, Object>) response.get("headers");
			String cookie = (String) headers.get("Set-Cookie");

			// 获取Cookie后再发一次请求
			headerMap.clear();
			headerMap.put("Cookie", cookie);
			response = HttpClientUtil.postWithParams(headerMap, url, paramMap);
			resultJson = (String) response.get("body");
			jsonObject = JSON.parseObject(resultJson);
		}
		return jsonObject;
	}

	/**
	 * 底层函数：将JSONObject解析成Song
	 *
	 * @param jsonObject
	 * @return
	 */
	private static Song baseParseObjectToSong(JSONObject jsonObject) throws IOException {
		// 1. 初步提取数据
		String songId = jsonObject.getString("id");
		String songName = jsonObject.getString("name");
		Integer songDuration = jsonObject.getInteger("duration");
		JSONArray artistJsonArray = jsonObject.getJSONArray("artists");
		JSONObject albumJsonObject = jsonObject.getJSONObject("album");
		// 2. 数据进一步处理
		// 2-1. 提取作者信息
		List<Artist> artistList = new ArrayList<>();
		if (artistJsonArray != null) {
			for (Object obj : artistJsonArray) {
				JSONObject artistJsonObj = (JSONObject) obj;
				String artistId = artistJsonObj.getString("id");
				String artistName = artistJsonObj.getString("name");
				String artistPictureUrl = artistJsonObj.getString("picUrl");
				Artist artist = new Artist()
						.setArtistId(artistId)
						.setArtistName(artistName)
						.setArtistPictureUrl(artistPictureUrl);
				artistList.add(artist);
			}
		}

		// 2-2. 提取专辑信息
		String albumId = null;
		Album album = null;
		if (albumJsonObject != null) {
			albumId = albumJsonObject.getString("id");
			String albumName = albumJsonObject.getString("name");
			// 处理别名列表
			String albumAlias;
			StringBuilder albumAliasSb = new StringBuilder();
			JSONArray albumAliasJsonArray = albumJsonObject.getJSONArray("alias");

			if (albumAliasJsonArray != null) {
				for (int index = 0; index < albumAliasJsonArray.size(); index++) {
					String subAlbumAlias = albumAliasJsonArray.getString(index);
					if (index != albumAliasJsonArray.size() - 1) {
						albumAliasSb.append(subAlbumAlias).append(", ");
					}
				}
			}
			albumAlias = albumAliasSb.toString();

			String albumPictureUrl = albumJsonObject.getString("picUrl");
			Date albumPublishTime = null;
			try {
				albumPublishTime = albumJsonObject.getDate("publishTime");
			} catch (Exception e) {
				// 不作处理
			}
			album = new Album()
					.setAlbumId(albumId)
					.setAlbumName(albumName)
					.setAlbumAlias(albumAlias)
					.setAlbumPictureUrl(albumPictureUrl)
					.setAlbumPublicTime(albumPublishTime);
		}

		// 3. 获取歌词
		String songLyric = baseGetSongLyric(songId);
		// 4. 数据封装并返回
		return new Song()
				.setSongId(songId)
				.setSongName(songName)
				.setSongDuration(songDuration)
				.setSongLyric(songLyric)
				.setArtistList(artistList)
				.setAlbumId(albumId)
				.setAlbum(album);
	}

	/**
	 * 底层函数：将JSONArray解析成List<Song>
	 *
	 * @param jsonArray
	 * @return
	 */
	private static List<Song> baseParseArrayToSongList(JSONArray jsonArray) throws IOException {
		List<Song> songList = new ArrayList<>();
		for (Object object : jsonArray) {
			JSONObject jsonObject = (JSONObject) object;
			Song song = baseParseObjectToSong(jsonObject);
			songList.add(song);
		}
		return songList;
	}

	/**
	 * 获取音乐源
	 *
	 * @param songId
	 * @return
	 * @throws IOException
	 */
	public static String getSongUrl(String songId) throws IOException {
		int br = 128000;
		if ("low".contentEquals(audioQuality)) {
			br = 64000;
		} else if ("middle".contentEquals(audioQuality)) {
			br = 198000;
		} else if ("high".contentEquals(audioQuality)) {
			br = 320000;
		}
		// 先获取音乐源
		Map<String, Object> paramMap = new HashMap<>();
		paramMap.put("id", songId);
		paramMap.put("ids", "[" + songId + "]");
		paramMap.put("br", br);
		Map<String, Object> response = HttpClientUtil.getWithParams("http://music.163.com/api/song/enhance/player/url", paramMap);
		String jsonResult = (String) response.get("body");
		JSONObject jsonObject = JSON.parseObject(jsonResult);
		JSONArray jsonArray = jsonObject.getJSONArray("data");
		String url = ((JSONObject) jsonArray.get(0)).getString("url");
		// 处理空值
		if (url == null) {
			// 为空值，尝试用黑胶会员的Cookie去重新获取音乐源
			try {
				String cookie = "NMTID=00OZuaKwghCLXAtsE34gtKyckGBsfkAAAF9HV1F6A; JSESSIONID-WYYY=NstKzmYavRqDJHjYc2QwsxuPtERHym3YUrdT%2BjrF5wMEDDKnUC5qdTq1nbT5H%5CJmO0wXp9fa%2FC8GMOglIc59%2BTiUgtJDT%2FvsDj2AF%5CeFv7jbIQO5t2wyqo3IurvnsUXRcSixh4Sn0W6HEUBDIwB1PaNek397glRXOEbaTQg%2BH8XkbhSr%3A1636877416662; _iuqxldmzr_=32; _ntes_nnid=2864f8668397b5c9f83599aef317db84,1636875616704; _ntes_nuid=2864f8668397b5c9f83599aef317db84; WEVNSM=1.0.0; WNMCID=qadstn.1636875617591.01.0; WM_NI=yS%2BOiBLlk8GgP8xOBGUkwR3hktXFXXvtJN9vOnBO6LleewXVesEma2cHo69Tq0c4wMFmaexO8J%2F7QHlq2QEnyHIlMRbZjd%2Bw3vRSYWTgXLXh6gN7O7Ie5z5CTh4xnVZJUWk%3D; WM_TID=HECUaF51L4xEEFBAFVY6pkL3PE6batlK; WM_NIKE=9ca17ae2e6ffcda170e2e6ee94f65badbc9cbbe869b6bc8ba3c14e979b8fbbf479a5baa382c4218dbffc99e12af0fea7c3b92abb8de5b0ec4a90b4a5d9c66d94e8b8b0c14d97b5b9b3b264a9ac98a7db54b6ac86afbc7e8db28eb0b548b0bbab98db5ea7ad8b8ee43aa198a1d7bc60b3f0add5f64786b3af86fb45fc96e1d8d444b0affeb2c866bb98878dd05aa286a685aa7df1aa9eb4dc5d98b998addb64f6b982d8ce3d8bbbfb9bb84998b8fc99ed5e9a969bb8ea37e2a3; __remember_me=true; MUSIC_U=68d569e7617196730a9f0563aa08349c5e46de0749167a888dfbd2d89cf4fd381e8907c67206e1edd78b6050a17a35e705925a4e6992f61dfe3f0151024f9e31; __csrf=4e40f46ca89553267172ec3d2b42f3cd; ntes_kaola_ad=1";
				Map<String, String> headerMap = new HashMap<>();
				headerMap.put("Cookie", cookie);
				response = HttpClientUtil.getWithParams(headerMap, "http://music.163.com/api/song/enhance/player/url", paramMap);
				jsonResult = (String) response.get("body");
				jsonObject = JSON.parseObject(jsonResult);
				jsonArray = jsonObject.getJSONArray("data");
				url = ((JSONObject) jsonArray.get(0)).getString("url");
			} catch (Exception e) {
				url = null;
			}
			// 仍为空，说明确实没有音乐源
			if (url == null) {
				return "暂无音乐源";
			} else {
				return url;
			}
		} else {
			return url;
		}
	}

	/**
	 * 根据关键词搜索歌曲
	 *
	 * @param key    关键词
	 * @param offset 偏移量
	 * @return
	 * @throws IOException
	 */
	public static List<Song> listByKey(String key, Integer offset) throws IOException {
		JSONObject jsonObject = baseSearchSong(key, offset, songListCount, 1);
		// 解析，获取列表
		JSONArray songJsonArray = jsonObject.getJSONObject("result").getJSONArray("songs");
		if (songJsonArray != null) {
			return baseParseArrayToSongList(songJsonArray);
		} else {
			return new ArrayList<>();
		}

	}

	/**
	 * 根据音乐id，获取音乐详细信息
	 *
	 * @param songId
	 * @return
	 * @throws IOException
	 */
	public static Song getBySongIdAndSongName(String songId, String songName) throws IOException {
		JSONObject jsonObject = baseSearchSong(songId + songName, 0, 1, 1);
		JSONObject songJsonObject = null;
		try {
			// 解析，获取列表中的第一首
			songJsonObject = (JSONObject) jsonObject.getJSONObject("result").getJSONArray("songs").get(0);
			return baseParseObjectToSong(songJsonObject);
		}
		// 英文歌会报空指针异常，此时需要特殊处理
		catch (NullPointerException e1) {
			JSONArray jsonArray = null;
			jsonObject = baseSearchSong(songId, 0, 10, 1);
			try {
				jsonArray = jsonObject.getJSONObject("result").getJSONArray("songs");
			} catch (NullPointerException e2) {
				// 空指针，操作频繁，递归调用也不行，此处只能跳过
			}
			if (jsonArray == null) {
				jsonObject = baseSearchSong(songName, 0, 10, 1);
				try {
					jsonArray = jsonObject.getJSONObject("result").getJSONArray("songs");
				} catch (NullPointerException e3) {
					// 空指针，操作频繁，递归调用也不行，此处只能跳过
				}
			}
			// 如果一直是空指针，此时就只能返回了
			if (jsonArray != null) {
				for (Object object : jsonArray) {
					JSONObject jsonObj = (JSONObject) object;
					if (songName.contentEquals(jsonObj.getString("name")) && songId.contentEquals(jsonObj.getString("id"))) {
						songJsonObject = jsonObj;
						break;
					}
				}
				if (songJsonObject != null) {
					return baseParseObjectToSong(songJsonObject);
				} else {
					return null;
				}
			} else {
				System.out.println("网易云服务器问题，无法获取歌曲信息");
				return null;
			}
		}
	}

	/**
	 * 搜索热歌/新歌/飙升/抖音排行
	 *
	 * @param type
	 * @return
	 */
	public static List<Song> listFromBillboard(String type) throws IOException {
		String url = "https://music.163.com/discover/toplist";
		String id;
		// 热歌
		if ("hot".contentEquals(type)) {
			id = "3778678";
		}
		// 新歌
		else if ("new".contentEquals(type)) {
			id = "3779629";
		}
		// 飙升
		else if ("up".contentEquals(type)) {
			id = "19723756";
		}
		// 抖音排行榜
		else if ("tikTok".contentEquals(type)) {
			id = "2250011882";
		}
		// 参数错误
		else {
			id = "-1";
		}
		Map<String, Object> paramMap = new HashMap<>();
		paramMap.put("id", id);
		String xpath = "//div[@id='song-list-pre-cache']/ul/li/a";
		Map<String, Object> response = HttpClientUtil.getWithParams(url, paramMap);
		String html = (String) response.get("body");

		// 使用xpath提取出所有id
		Document doc = Jsoup.parse(html);
		Elements elements = Xsoup.compile(xpath).evaluate(doc).getElements();
		List<Song> songList = new ArrayList<>();
		for (int index = 0; index < songListCount; index++) {
			Element element = elements.get(index);
			String songId = element.attr("href").substring(9);
			String songName = element.text();
			// 获得歌曲的详细信息
			Song song = getBySongIdAndSongName(songId, songName);
			if (song != null) {
				songList.add(song);
			}
		}
		return songList;
	}

	public static void main(String[] args) throws IOException {
//		System.out.println(JSON.toJSON(listByKey("我怀念的孙燕姿", 0)));
//		Song song = getBySongIdAndSongName("287063", "我怀念的");
//		System.out.println(song);

//		List<Song> hotSongList = listFromBillboard("up");
//		System.out.println(hotSongList);

		JSONObject jsonObject = baseSearchSong("囍", 0, 10, 1);
		System.out.println(jsonObject);
	}
}
